package com.aliyun.heiyu.powermaster.server.manager;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.heiyu.common.util.DateUtil;
import com.aliyun.heiyu.common.util.PropertiesUtil;
import com.aliyun.heiyu.common.util.StringUtil;
import com.aliyun.heiyu.powermaster.server.annotation.Transactional;
import com.aliyun.heiyu.powermaster.server.dao.*;
import com.aliyun.heiyu.powermaster.server.dao.entity.*;
import com.aliyun.heiyu.powermaster.server.dao.impl.*;
//import com.aliyun.heiyu.powermaster.server.market.MarketDealer;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;

import static org.apache.commons.beanutils.BeanUtils.describe;

public class DeviceManagerImpl implements DeviceManager {
    private static Logger logger = LoggerFactory.getLogger(DeviceManagerImpl.class);

    private DeviceCommonDao deviceCommonDao;
    private DeviceSpecDao deviceServerDao;
    private DeviceSpecDao deviceCabinetDao;
    private DeviceSpecDao deviceAirDao;
    private DeviceSpecDao devicePduDao;
    private DeviceSpecDao deviceIpcDao;
    private RackConfigMetaDao rackConfigMetaDao;
    private Strategy2DeviceDao strategy2DeviceDao;
    private Map<String, DeviceSpecDao> deviceSpecDaoMap = new HashMap<>();

    private Map<String, DeviceCommonEntity> deviceCommonMap = new HashMap<>();
    private Map<String, Map<String, String>> deviceDetailMap = new HashMap<>();

    private static DeviceManagerImpl instance = new DeviceManagerImpl();

    //todo 设备类型做枚举
    private DeviceManagerImpl() {
        this.deviceCommonDao = new DeviceCommonImpl();
        this.deviceServerDao = new DeviceSpecServerImpl();
        this.deviceCabinetDao = new DeviceSpecCabinetImpl();
        this.deviceAirDao = new DeviceSpecAirImpl();
        this.devicePduDao = new DeviceSpecPduImpl();
        this.rackConfigMetaDao = new RackConfigMetaImpl();
        this.strategy2DeviceDao = new Strategy2DeviceImpl();
        this.deviceIpcDao   = new DeviceSpecIpcImpl();
        this.deviceSpecDaoMap.put("server", deviceServerDao);
        this.deviceSpecDaoMap.put("cabinet", deviceCabinetDao);
        this.deviceSpecDaoMap.put("air", deviceAirDao);
        this.deviceSpecDaoMap.put("pdu", devicePduDao);
        this.deviceSpecDaoMap.put("ipc", deviceIpcDao);

        initCache();
    }

    public static DeviceManagerImpl getInstance() {
        return instance;
    }

    /**
     * 初始化缓存
     */
    public void initCache() {
        deviceCommonMap.clear();
        deviceDetailMap.clear();
        List<DeviceCommonEntity> deviceCommons = deviceCommonDao.findAllEntity();
        if (deviceCommons != null) {
            for (DeviceCommonEntity deviceCommonEntity : deviceCommons) {
                String deviceId = deviceCommonEntity.getDevice_id();
                deviceCommonMap.put(deviceId, deviceCommonEntity);
            }
        }
        List<DeviceSpecDao> deviceSpecDaoList = new ArrayList<>(deviceSpecDaoMap.values());
        for (DeviceSpecDao deviceSpecDao : deviceSpecDaoList) {
            List<Map<String, String>> deviceSpecList = deviceSpecDao.findAll();
            if (deviceSpecList == null || deviceSpecDaoList.size() < 1) {
                continue;
            }
            for (Map<String, String> map : deviceSpecList) {
                String deviceSn = map.get("device_id");
                if (deviceCommonMap.containsKey(deviceSn)) {
                    DeviceCommonEntity deviceCommonEntity = deviceCommonMap.get(deviceSn);
                    Map<String, String> deviceCommon1 = null;
                    try {
                        deviceCommon1 = BeanUtils.describe(deviceCommonEntity);
                        deviceCommon1.putAll(map);
                        deviceDetailMap.put(deviceSn, deviceCommon1);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    @Override
    @Transactional
    public boolean insert(Map<String, String> map) throws SQLException {
        insert1(map);

        //缓存新增
        String deviceSn = map.get("device_id");
        map = fetchFromDB(deviceSn);
        updateCache(deviceSn,map);
        return true;
    }

    @Override
    @Transactional
    public boolean update(Map<String, String> map) throws SQLException {
        String deviceId = map.get("device_id");
        String type = map.get("type");
        //联动元数据表
        //todo 完善修改字段
        if ("server".equals(type)) {
            DeviceCommonEntity deviceCommonEntity = findOneDeviceCommon(deviceId);
            String cabinetSn = deviceCommonEntity.getLocated_cabinet();
            String newCabinetSn = map.get("located_cabinet");
            String ip = map.get("ip");
            ServerThunderDto serverThunderDto = new ServerThunderDto();
            serverThunderDto.setSn(deviceId);
            serverThunderDto.setIp(ip);
            if (newCabinetSn.equals(cabinetSn)) {
                modifyMetaSserver(cabinetSn, "UPDATE", serverThunderDto);
            } else {
                modifyMetaSserver(cabinetSn, "DELETE", serverThunderDto);
                modifyMetaSserver(newCabinetSn, "ADD", serverThunderDto);
            }

        }

        //修改deviceSpecific
        deviceSpecDaoMap.get(type).update(map);

        //修改deviceCommon
        deviceCommonDao.update(map);

        //缓存修改
        map = fetchFromDB(deviceId);
        updateCache(deviceId, map);
        return true;
    }

    @Override
    @Transactional
    public boolean delete(String[] deviceIds) throws SQLException {
        //遍历做删除
        for (String deviceId : deviceIds) {
            DeviceCommonEntity deviceCommonEntity = deviceCommonMap.get(deviceId);
            if (deviceCommonEntity == null) {
                continue;
            }
            String type = deviceCommonEntity.getType();
            if ("server".equals(type)) {
                Map<String, String> map = deviceDetailMap.get(deviceId);
                if(null != map && !"arm".equals(map.get("server_type"))){
                    deleteMetaData(deviceId);
                }
            }
            if ("cabinet".equals(type)) {
                deleteMetaData(deviceId);
            }

            //删除策略
            strategy2DeviceDao.deleteByDeviceId(deviceId);

            //删除在业务下
           // MarketDealer.getInstance().removeDeviceFromMarket(deviceId);

            deviceSpecDaoMap.get(type).delete(deviceId);
            deviceCommonDao.delete(deviceId);
        }

        //更新缓存
        for (String deviceId : deviceIds) {
            deviceCommonMap.remove(deviceId);
            deviceDetailMap.remove(deviceId);
        }
        return true;
    }

    @Override
    @Transactional
    public boolean insertAll(List<Map<String, String>> list) throws SQLException {
        for(Map<String, String> map : list){
            insert1(map);
        }
        for(Map<String, String> map : list){
            String deviceSn = map.get("device_id");
            map = fetchFromDB(deviceSn);
            updateCache(deviceSn,map);
        }
        return true;
    }

    @Override
    public int getTotalCount(String cabinetSn, String condition, String type, String business, String usageStatus, String locationId) {
        Object[] types = null;
        if (type != null && !"".equals(type)) {
            types = type.split(",");
        }
        int totalCount = deviceCommonDao.getTotalCountByCondition(cabinetSn, condition, types, business, usageStatus,locationId);
        return totalCount;
    }

    @Override
    public List<DeviceCommonEntity> getDeviceCommonByCondition(String cabinetSn, String condition, String type, String business, String usageStatus, int pageNum, int pageSize,int totalCount,String locationId) {
        List<DeviceCommonEntity> deviceCommonList = new ArrayList<>();
        if (totalCount == 0) {
            return deviceCommonList;
        }
        if (pageSize == -1) {
            return getDeviceCommonByCondition(cabinetSn, condition, type, business, usageStatus,locationId);
        }
        if (pageSize <= 0) {
            pageSize = 10;
        }
       if (pageNum <= 1) {
            pageNum = 1;
        }

        Object[] types = null;
        if (type != null && !"".equals(type)) {
            types = type.split(",");
        }

        List<DeviceCommonEntity> devicecommonList = deviceCommonDao.findByCondition(cabinetSn, condition, types, business, usageStatus, (pageNum - 1) * pageSize, pageSize,locationId);
        if (devicecommonList == null) {
            devicecommonList = new ArrayList<>();
        }
        return devicecommonList;
    }

    @Override
    public List<DeviceCommonEntity> getDeviceCommonByCondition(String cabinetSn, String condition, String type, String business, String usageStatus, String locationId) {
        Object[] types = null;
        if (type != null && !"".equals(type)) {
            types = type.split(",");
        }
        List<DeviceCommonEntity> devicecommonList = deviceCommonDao.findByCondition(cabinetSn, condition, types, business, usageStatus,locationId);
        if (devicecommonList == null) {
            devicecommonList = new ArrayList<>();
        }
        return devicecommonList;
    }

    @Override
    public List<DeviceCommonEntity> findAllDeviceCommon() {
        if (deviceCommonMap.isEmpty()) {
            initCache();
        }
        return new ArrayList<>(deviceCommonMap.values());
    }

    @Override
    public List<DeviceCommonEntity> findByType(String type) {
        List<DeviceCommonEntity> list = deviceCommonDao.findByType(type);
        if (list == null) {
            list = new ArrayList<>();
        }
        return list;
    }

    @Override
    public Map<String, String> findOneDeviceDetail(String deviceId) {
        //缓存查询
        Map<String, String> deviceDetail = deviceDetailMap.get(deviceId);
        //缓存中没有，从数据库中查询
        if (deviceDetail == null) {
            deviceDetail = fetchFromDB(deviceId);
            if (deviceDetail != null) {
                updateCache(deviceId, deviceDetail);
            }
        }
        return deviceDetailMap.get(deviceId);
    }

    @Override
    public List<Map<String, String>> findDeviceSnByType(String types) {
        return deviceCommonDao.findByTypes(types);
    }

    @Override
    public DeviceCommonEntity findOneDeviceCommon(String deviceId) {
        //缓存查询
        DeviceCommonEntity deviceCommonEntity = deviceCommonMap.get(deviceId);
        //缓存中没有，从数据库中查询
        if (deviceCommonEntity == null) {
            Map<String, String> deviceDetail = fetchFromDB(deviceId);
            //更新缓存
            if (deviceDetail != null) {
                updateCache(deviceId, deviceDetail);
            }
        }
        return deviceCommonMap.get(deviceId);
    }

    @Override
    public List<DeviceCommonEntity> findByLabel(String label) {
        List<DeviceCommonEntity> devices = new ArrayList<>();
        String[] snsArray = PropertiesUtil.getArrayValue("labelStore.properties", label);
        if (snsArray.length != 0) {
            devices = getListDeviceCommon(snsArray);
        } else {
            //label为机柜编号查询
            devices = findByCabinet(label);
            //label为设备编号查询
            if (devices.isEmpty()) {
                DeviceCommonEntity deviceCommon = findOneDeviceCommon(label);
                if (deviceCommon != null) {
                    devices.add(deviceCommon);
                }
            }
        }
        if (devices.isEmpty()) {
            logger.warn("please check label ");
        }
        return devices;
    }

    @Override
    public String[] findDeviceIdArrayByLabel(String label) {
        List<DeviceCommonEntity> devices = findByLabel(label);
        ArrayList<String> deviceIdList = new ArrayList<>();
        for (DeviceCommonEntity deviceCommonEntity : devices) {
            deviceIdList.add(deviceCommonEntity.getDevice_id());
        }
        String[] array = deviceIdList.toArray(new String[0]);
        return array;
    }

    @Override
    public int getCountOfCabinet(String deviceId) {
        return deviceCommonDao.getCountOfCabinet(deviceId);
    }

    @Override
    public List<DeviceCommonEntity> findByCabinet(String cabinet) {
        List<DeviceCommonEntity> list = deviceCommonDao.findByCabinet(cabinet);
        if (list == null) {
            list = new ArrayList<>();
        }
        return list;
    }

    @Override
    public boolean checkDiviceExit(String deviceId) {
        DeviceCommonEntity device = findOneDeviceCommon(deviceId);
        return device == null ? false : true;
    }

    @Override
    public boolean checkDiviceIpExit(String ip) {
        DeviceCommonEntity device = deviceCommonDao.findByIp(ip);;
        return device == null ? false : true;
    }

    @Override
    public boolean checkLocatedIdExit(String locatedId) {
        DeviceCommonEntity device = deviceCommonDao.findByLocatedId(locatedId);
        return device == null ? false : true;
    }

    @Override
    public boolean checkDeleteParams(String[] deviceIds) {
        boolean b = true;
        for (String deviceId : deviceIds) {
            DeviceCommonEntity deviceCommonEntity = findOneDeviceCommon(deviceId);
            if (deviceCommonEntity == null) {
                continue;
            }
            String type = deviceCommonEntity.getType();
            if ("cabinet".equals(type)) {
                List<DeviceCommonEntity> deviceList = findByCabinet(deviceId);
                b = deviceList.size() > 0 ? false : true;
            }
            if (!b) {
                logger.debug("机柜 {} 下有设备不能删除" , deviceId );
                break;
            }
        }
        return b;
    }

    @Override
    public Map<String, Map<String, String>> getDeviceDetailByCondition(String cabinetSn, String condition, String type, String business, String usageStatus, String locationId) {
        Map<String, Map<String, String>> map = new LinkedHashMap<>();
        List<DeviceCommonEntity> deviceCommonlist = getDeviceCommonByCondition(cabinetSn, condition, type, business, usageStatus, locationId);
        if (deviceCommonlist != null && deviceCommonlist.size() > 0) {
            for (DeviceCommonEntity deviceCommon : deviceCommonlist) {
                Map<String, String> deviceMap = deviceDetailMap.get(deviceCommon.getDevice_id());
                if (deviceMap == null || deviceMap.isEmpty()) {
                    continue;
                }
                map.put(deviceCommon.getDevice_id(), deviceMap);
            }
        }
        return map;
    }

    @Override
    public boolean modifyUsageStatus(String deviceId, String usageStatus) {
        //todo
        try {
            deviceCommonDao.modifyUsageStatus(deviceId, usageStatus);
        } catch (SQLException e) {
            return false;
        }

        //更新缓存
        DeviceCommonEntity entity = deviceCommonMap.get(deviceId);
        if (entity != null) {
            entity.setUsage_status(usageStatus);
        }
        Map<String, String> map = deviceDetailMap.get(deviceId);
        if (map != null) {
            map.put("usage_status", usageStatus);
        }

        return true;
    }

    @Override
    public  Map<String, Map<String, String>> findAllDeviceDetailMap() {
        if (deviceDetailMap.isEmpty()) {
            initCache();
        }
        return deviceDetailMap;
    }

    @Override
    public int usedServerAmount() {
        Map<String, String> map = deviceCommonDao.usedServerAmount();
        if(map == null || map.get("count") == null){
            return 0;
        }
        return StringUtil.convertInt(map.get("count"));
    }

    @Override
    public int totalServerAmount() {
        Map<String, String> map = deviceCommonDao.totalServerAmount();
        if(map == null || map.get("count") == null){
            return 0;
        }
        return StringUtil.convertInt(map.get("count"));
    }

    /**
     * 从数据库中直接查询
     *
     * @param deviceId 设备ID
     * @return
     */
    private Map<String, String> fetchFromDB(String deviceId) {
        Map<String, String> deviceCommon1 = null;
        DeviceCommonEntity deviceCommonEntity = deviceCommonDao.findOneEntity(deviceId);
        if (deviceCommonEntity == null) {
            return null;
        }
        String deviceType = deviceCommonEntity.getType();
        DeviceSpecDao deviceSpecDao = deviceSpecDaoMap.get(deviceType);
        if (deviceSpecDao == null) {
            return null;
        }
        Map<String, String> deviceSpecific = deviceSpecDaoMap.get(deviceType).findOne(deviceId);
        if (deviceSpecific != null) {
            try {
                deviceCommon1 = describe(deviceCommonEntity);
                deviceCommon1.putAll(deviceSpecific);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
        return deviceCommon1;
    }

    private boolean insert1(Map<String, String> map) throws SQLException {
        String deviceSn = map.get("device_id");
        String type = map.get("type");
        String serverType = map.get("server_type");
        //联动元数据表
        if ("server".equals(type) && !"arm".equals(serverType)) {
            String ip = map.get("ip");
            String cabinetSn = map.get("located_cabinet");
            String hostName = map.get("host_name");
            ServerThunderDto serverThunderDto = new ServerThunderDto();
            serverThunderDto.setSn(deviceSn);
            serverThunderDto.setCollect(true);
            serverThunderDto.setIp(ip);
            serverThunderDto.setHostname(hostName);
            serverThunderDto.setCapping(false);
            modifyMetaSserver(cabinetSn, "ADD", serverThunderDto);
        }
        if ("cabinet".equals(type)) {
            addMetaCabinet(map);
        }

        //新增deviceSpecific
        deviceSpecDaoMap.get(type).insert(map);

        //新增deviceCommon
        deviceCommonDao.insert(map);

        return true;
    }

    private void updateCache(String deviceId, Map<String, String> deviceDetail) {
        deviceDetailMap.put(deviceId, deviceDetail);
        try {
            DeviceCommonEntity deviceCommonEntity = new DeviceCommonEntity();
            BeanUtils.populate(deviceCommonEntity, deviceDetail);
            deviceCommonMap.put(deviceId, deviceCommonEntity);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    /**
     * 设备联动，删除采集表中的元数据--服务器或者机柜
     *
     * @param deviceId
     * @return
     */
    private void deleteMetaData(String deviceId) throws SQLException {
        DeviceCommonEntity deviceCommonEntity = findOneDeviceCommon(deviceId);
        if(deviceCommonEntity == null){
            return;
        }
        String type = deviceCommonEntity.getType();
        if ("server".equals(type)) {
            String cabinetSn = deviceCommonEntity.getLocated_cabinet();
            ServerThunderDto server = new ServerThunderDto();
            server.setSn(deviceId);
            modifyMetaSserver(cabinetSn, "DELETE", server);
        } else if ("cabinet".equals(type)) {
            removeMetaCabinet(deviceId);
        }
    }

    /**
     * @param cabinetSn 机柜编号
     * @param action    删除/修改/增加 服务器 ADD/DELETE/UPDATE
     * @param server    服务器serverThunderDto对象
     * @throws SQLException
     */
    //todo  动作做枚举或常量
    private void modifyMetaSserver(String cabinetSn, String action, ServerThunderDto server) throws SQLException {
        RackConfigMetaEntity rackConfigMeta = rackConfigMetaDao.findOneByCabinet(cabinetSn);
        //linshi
        if(rackConfigMeta == null){
            return;
        }
        byte[] meta = rackConfigMeta.getMeta();
        String s = new String(meta);
        RackConfig rackConfig = JSONObject.parseObject(s, RackConfig.class);
        List<ServerThunderDto> servers = rackConfig.getServers();
        String sn = server.getSn();
        if (sn == null || sn.isEmpty()) {
            logger.error("sn is emptied");
            return;
        }

        if ("ADD".equals(action)) {
            servers.add(server);
        } else if ("UPDATE".equals(action)) {
            for (ServerThunderDto serverThunderDto : servers) {
                String oldSn = serverThunderDto.getSn();
                if (sn.equals(oldSn)) {
                    //todo 修改字段待定
                    serverThunderDto.setIp(server.getIp());
                    break;
                }
            }
        } else if ("DELETE".equals(action)) {
            for (ServerThunderDto serverThunderDto : servers) {
                String oldSn = serverThunderDto.getSn();
                if (sn.equals(oldSn)) {
                    servers.remove(serverThunderDto);
                    break;
                }
            }
        }

        rackConfigMeta.setMeta(rackConfig.toString().getBytes());
        String time = DateUtil.millisecondToDate(System.currentTimeMillis());
        rackConfigMeta.setGmt_modified(Timestamp.valueOf(time));
        rackConfigMetaDao.update(rackConfigMeta);

    }

    /**
     * 联动采集元数据表，增加一个机柜
     *
     * @param map 机柜数据的Map集合
     */
    private void addMetaCabinet(Map<String, String> map) throws SQLException {
        String cabinetSn = map.get("device_id");
        RackConfigMetaEntity oneByCabinet = rackConfigMetaDao.findOneByCabinet(cabinetSn);
        if (oneByCabinet != null) {
            logger.error("采集表中该机柜已经存在");
            return;
        }
        String time = DateUtil.millisecondToDate(System.currentTimeMillis());
        RackConfigMetaEntity rackConfigMetaEntity = new RackConfigMetaEntity();
        rackConfigMetaEntity.setRack_code(cabinetSn);
        rackConfigMetaEntity.setGmt_create(Timestamp.valueOf(time));
        rackConfigMetaEntity.setGmt_modified(Timestamp.valueOf(time));
        ArrayList<ServerThunderDto> list = new ArrayList<>();
        RackConfig rackConfig = new RackConfig();
        rackConfig.setCapping(false);
        rackConfig.setCollect(true);
        String room = map.get("room");
        if (room != null) {
            rackConfig.setIdc(room);
        }
        rackConfig.setServers(list);
        rackConfig.setRackCode(cabinetSn);
        String rated_power = map.get("rated_power");
        if (rated_power != null) {
            rackConfig.setRatedPower(rated_power);
        } else {
            rackConfig.setRatedPower("0");
        }
        rackConfigMetaEntity.setMeta(rackConfig.toString().getBytes());

        rackConfigMetaDao.insert(rackConfigMetaEntity);

    }

    /**
     * 联动采集元数据表，删除一个机柜
     *
     * @param cabinetSn
     */
    private void removeMetaCabinet(String cabinetSn) throws SQLException {
        rackConfigMetaDao.delete(cabinetSn);
    }

    private List<DeviceCommonEntity> getListDeviceCommon(String[] snsArray) {
        List<DeviceCommonEntity> devices = new ArrayList<>();
        for (String sn : snsArray) {
            DeviceCommonEntity deviceCommon = findOneDeviceCommon(sn);
            if (deviceCommon != null) {
                devices.add(deviceCommon);
            }
        }
        return devices;
    }


    public static void main(String[] args){
        DeviceManagerImpl instance = DeviceManagerImpl.getInstance();
        Map<String, String> map = instance.fetchFromDB("SN778");
        System.out.print(map);
    }

}
